/*
	Input.cpp

	Contains the code to handle the low-level user input functionality

	Author: Aidan Goddard 8/8/14
*/

#include"Input.h"
#include"EngineState.h"
#include"Engine.h"
#include"SnowstormXEngineVars.h"

#include<cstdint>
#include<Window.h>
#include<Xinput.h>

#pragma comment(lib, "XINPUT9_1_0.lib")

///////////////////////////////////////////////
// static data
 
uint32_t Snowstorm::Input::KEY_STATES[256] = {0};
int Snowstorm::Input::MOUSE_X = 0;
int Snowstorm::Input::MOUSE_Y = 0;
int Snowstorm::Input::MOUSE_Z = 0;
EVENT Snowstorm::Input::WINDOW_CLOSE_EVENT_HANDLER = NULL;
EVENT Snowstorm::Input::KEY_PRESS_EVENT_HANDLERS[256] = {NULL};
EVENT Snowstorm::Input::KEY_RELEASE_EVENT_HANDLERS[256] = {NULL};
EVENT Snowstorm::Input::KEY_HELD_EVENT_HANDLERS[256] = {NULL};


uint32_t Snowstorm::Input::controller_states[XUSER_MAX_COUNT] = {0};
float Snowstorm::Input::xbox_deadzone_trigger = XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
float Snowstorm::Input::xbox_deadzone_lp = XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE;
float Snowstorm::Input::xbox_deadzone_ln = XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE*-1;
float Snowstorm::Input::xbox_deadzone_rp = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE;
float Snowstorm::Input::xbox_deadzone_rn = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE*-1;
double Snowstorm::Input::frame_timer = 5;

///////////////////////////////////////////////
// events
void Snowstorm::Input::KeyPressEvent(Snowstorm::Window *w, uint8_t virtual_key)
{
	// mark the key as newly pressed
	Snowstorm::Input::KEY_STATES[virtual_key] = KEY_NEW_PRESS;
	return;
}

void Snowstorm::Input::KeyReleaseEvent(Snowstorm::Window *w, uint8_t virtual_key)
{
	// mark the key as newly released
	Snowstorm::Input::KEY_STATES[virtual_key] = KEY_NEW_RELEASE;
	return;
}

void Snowstorm::Input::MouseEvent(Snowstorm::Window *w, int delta_x, int delta_y, short delta_z)
{
	// get values
	Snowstorm::Input::MOUSE_X += delta_x;
	Snowstorm::Input::MOUSE_Y += delta_y;
	Snowstorm::Input::MOUSE_Z += delta_z;
	return;
}

void Snowstorm::Input::MouseMoveEvent(Snowstorm::Window *w, WPARAM wparam, LPARAM lparam)
{
	Snowstorm::Engine::ev->screen_x = LOWORD(lparam);//lparam & 0xffff;
	Snowstorm::Engine::ev->screen_y = HIWORD(lparam);//(int)(lparam >> 16);
	return;
}

void Snowstorm::Input::WindowCloseClicked(Snowstorm::Window *w, WPARAM wparam, LPARAM lparam)
{
	// call the installed handler if it exists
	EVENT e = WINDOW_CLOSE_EVENT_HANDLER;
	if(e)
	{
		(*e)(Snowstorm::Engine::ev, 0);	// always called from the master thread
	}

	// otherwise, just exit
	else
	{
		Snowstorm::EngineState::engine_state.cmd_exit = true;
	}

	// finished
	return;
}

void Snowstorm::Input::PollKeyboardInput(int base, int limit, uint32_t SNOWSTORM_ENGINE_TID)
{
	// check each key
	for(int i = base; i < limit; i++)
	{
		// get pointer to key states value & the value itself
		uint32_t *key_state_ptr = &KEY_STATES[i];
		uint32_t key_state = *key_state_ptr;

		// get ptr to engine keystate qword and the bits position
		uint64_t *qword = &(Snowstorm::Engine::ev->KEYSTATE[i >> 6]);
		uint64_t bits = 1ULL << (i & 0x3f);

		// check for fresh keypress condition
		if(key_state & KEY_NEW_PRESS)
		{
			// set the global keystate
			*key_state_ptr = 1;
			*qword |= bits;

			// check if an event is installed for it
			EVENT e = Snowstorm::Input::KEY_PRESS_EVENT_HANDLERS[i];
			if(e)
			{
				// call the event
				(*e)(Snowstorm::Engine::ev, SNOWSTORM_ENGINE_TID);
			}
		}

		// check for key still held condition
		if(key_state)
		{
			*key_state_ptr += 1;

			// check if an event is installed for it
			EVENT e = Snowstorm::Input::KEY_HELD_EVENT_HANDLERS[i];
			if(e)
			{
				// call the event
				(*e)(Snowstorm::Engine::ev, SNOWSTORM_ENGINE_TID);
			}
		}

		// check for key release condition
		if(key_state & KEY_NEW_RELEASE)
		{
			*key_state_ptr = 0;
			*qword &= ~(bits);

			// check if an event is installed for it
			EVENT e = Snowstorm::Input::KEY_RELEASE_EVENT_HANDLERS[i];
			if(e)
			{
				// call the event
				(*e)(Snowstorm::Engine::ev, SNOWSTORM_ENGINE_TID);
			}
		}
	}

	// finished
	return;
}

void Snowstorm::Input::PollXboxControllerInput()
{
	// get mouse mickey values
	Snowstorm::Engine::ev->mickey_x = Snowstorm::Input::MOUSE_X;
	Snowstorm::Engine::ev->mickey_y = Snowstorm::Input::MOUSE_Y;
	Snowstorm::Engine::ev->mickey_z = Snowstorm::Input::MOUSE_Z;

	// reset internal mouse counters
	Snowstorm::Input::MOUSE_X = 0;
	Snowstorm::Input::MOUSE_Y = 0;
	Snowstorm::Input::MOUSE_Z = 0;


	// get xbox controller input
	// check xbox controller connection state every 5 seconds (ish)
	// see http://msdn.microsoft.com/en-gb/library/windows/desktop/ee417001(v=vs.85).aspx
	if(Snowstorm::Input::frame_timer >= 5)
	{
		// check the connected controllers
		// check each controller port
		for(int i = 0; i < XUSER_MAX_COUNT; i++)
		{
			// state structure
			XINPUT_STATE state;
			ZeroMemory(&state, sizeof(XINPUT_STATE));

			// get the state of the controller
			DWORD result = XInputGetState(i, &state);

			// check state
			if(result == ERROR_SUCCESS)
			{
				// controller is connected
				Snowstorm::Input::controller_states[i] = 1;
			}
			else
			{
				// controller is not connected
				Snowstorm::Input::controller_states[i] = 0;
			}
		}

		// reset the frame timer
		Snowstorm::Input::frame_timer = 0;
	}
	else
	{
		Snowstorm::Input::frame_timer += Snowstorm::Engine::ev->frame_time;
	}

	// get controller states
	// for each controller
	for(int i = 0; i < XUSER_MAX_COUNT; i++)
	{
		// check if controller is connected
		if(Snowstorm::Input::controller_states[i] > 0)
		{
			// get the controller state
			XINPUT_STATE controller_state;
			ZeroMemory(&controller_state, sizeof(XINPUT_STATE));
			DWORD result = XInputGetState(i, &controller_state);
			// check state
			if(result == ERROR_SUCCESS)
			{
				// controller is connected
				// check buttons
				// A button
				if(controller_state.Gamepad.wButtons & XINPUT_GAMEPAD_A)
				{
					if(!Snowstorm::Input::KEY_STATES[VK_XBOXA])
						Snowstorm::Input::KEY_STATES[VK_XBOXA] = KEY_NEW_PRESS;
				}
				else
				{
					if(Snowstorm::Input::KEY_STATES[VK_XBOXA])
						Snowstorm::Input::KEY_STATES[VK_XBOXA] = KEY_NEW_RELEASE;
				}

				// B button
				if(controller_state.Gamepad.wButtons & XINPUT_GAMEPAD_B)
				{
					if(!Snowstorm::Input::KEY_STATES[VK_XBOXB])
						Snowstorm::Input::KEY_STATES[VK_XBOXB] = KEY_NEW_PRESS;
				}
				else
				{
					if(Snowstorm::Input::KEY_STATES[VK_XBOXB])
						Snowstorm::Input::KEY_STATES[VK_XBOXB] = KEY_NEW_RELEASE;
				}

				// X button
				if(controller_state.Gamepad.wButtons & XINPUT_GAMEPAD_X)
				{
					if(!Snowstorm::Input::KEY_STATES[VK_XBOXX])
						Snowstorm::Input::KEY_STATES[VK_XBOXX] = KEY_NEW_PRESS;
				}
				else
				{
					if(Snowstorm::Input::KEY_STATES[VK_XBOXX])
						Snowstorm::Input::KEY_STATES[VK_XBOXX] = KEY_NEW_RELEASE;
				}

				// Y button
				if(controller_state.Gamepad.wButtons & XINPUT_GAMEPAD_Y)
				{
					if(!Snowstorm::Input::KEY_STATES[VK_XBOXY])
						Snowstorm::Input::KEY_STATES[VK_XBOXY] = KEY_NEW_PRESS;
				}
				else
				{
					if(Snowstorm::Input::KEY_STATES[VK_XBOXY])
						Snowstorm::Input::KEY_STATES[VK_XBOXY] = KEY_NEW_RELEASE;
				}

				// START button
				if(controller_state.Gamepad.wButtons & XINPUT_GAMEPAD_START)
				{
					if(!Snowstorm::Input::KEY_STATES[VK_XBOXSTART])
						Snowstorm::Input::KEY_STATES[VK_XBOXSTART] = KEY_NEW_PRESS;
				}
				else
				{
					if(Snowstorm::Input::KEY_STATES[VK_XBOXSTART])
						Snowstorm::Input::KEY_STATES[VK_XBOXSTART] = KEY_NEW_RELEASE;
				}

				// BACK button
				if(controller_state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK)
				{
					if(!Snowstorm::Input::KEY_STATES[VK_XBOXBACK])
						Snowstorm::Input::KEY_STATES[VK_XBOXBACK] = KEY_NEW_PRESS;
				}
				else
				{
					if(Snowstorm::Input::KEY_STATES[VK_XBOXBACK])
						Snowstorm::Input::KEY_STATES[VK_XBOXBACK] = KEY_NEW_RELEASE;
				}

				// LB button
				if(controller_state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER)
				{
					if(!Snowstorm::Input::KEY_STATES[VK_XBOXLB])
						Snowstorm::Input::KEY_STATES[VK_XBOXLB] = KEY_NEW_PRESS;
				}
				else
				{
					if(Snowstorm::Input::KEY_STATES[VK_XBOXLB])
						Snowstorm::Input::KEY_STATES[VK_XBOXLB] = KEY_NEW_RELEASE;
				}

				// RB button
				if(controller_state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER)
				{
					if(!Snowstorm::Input::KEY_STATES[VK_XBOXRB])
						Snowstorm::Input::KEY_STATES[VK_XBOXRB] = KEY_NEW_PRESS;
				}
				else
				{
					if(Snowstorm::Input::KEY_STATES[VK_XBOXRB])
						Snowstorm::Input::KEY_STATES[VK_XBOXRB] = KEY_NEW_RELEASE;
				}

				// LT button
				if(controller_state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB)
				{
					if(!Snowstorm::Input::KEY_STATES[VK_XBOXLT])
						Snowstorm::Input::KEY_STATES[VK_XBOXLT] = KEY_NEW_PRESS;
				}
				else
				{
					if(Snowstorm::Input::KEY_STATES[VK_XBOXLT])
						Snowstorm::Input::KEY_STATES[VK_XBOXLT] = KEY_NEW_RELEASE;
				}

				// RT button
				if(controller_state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB)
				{
					if(!Snowstorm::Input::KEY_STATES[VK_XBOXRT])
						Snowstorm::Input::KEY_STATES[VK_XBOXRT] = KEY_NEW_PRESS;
				}
				else
				{
					if(Snowstorm::Input::KEY_STATES[VK_XBOXRT])
						Snowstorm::Input::KEY_STATES[VK_XBOXRT] = KEY_NEW_RELEASE;
				}

				// DPAD UP button
				if(controller_state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP)
				{
					if(!Snowstorm::Input::KEY_STATES[VK_XBOXDPADUP])
						Snowstorm::Input::KEY_STATES[VK_XBOXDPADUP] = KEY_NEW_PRESS;
				}
				else
				{
					if(Snowstorm::Input::KEY_STATES[VK_XBOXDPADUP])
						Snowstorm::Input::KEY_STATES[VK_XBOXDPADUP] = KEY_NEW_RELEASE;
				}

				// DPAD DOWN button
				if(controller_state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN)
				{
					if(!Snowstorm::Input::KEY_STATES[VK_XBOXDPADDOWN])
						Snowstorm::Input::KEY_STATES[VK_XBOXDPADDOWN] = KEY_NEW_PRESS;
				}
				else
				{
					if(Snowstorm::Input::KEY_STATES[VK_XBOXDPADDOWN])
						Snowstorm::Input::KEY_STATES[VK_XBOXDPADDOWN] = KEY_NEW_RELEASE;
				}

				// DPAD LEFT button
				if(controller_state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT)
				{
					if(!Snowstorm::Input::KEY_STATES[VK_XBOXDPADLEFT])
						Snowstorm::Input::KEY_STATES[VK_XBOXDPADLEFT] = KEY_NEW_PRESS;
				}
				else
				{
					if(Snowstorm::Input::KEY_STATES[VK_XBOXDPADLEFT])
						Snowstorm::Input::KEY_STATES[VK_XBOXDPADLEFT] = KEY_NEW_RELEASE;
				}

				// DPAD RIGHT button
				if(controller_state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT)
				{
					if(!Snowstorm::Input::KEY_STATES[VK_XBOXDPADRIGHT])
						Snowstorm::Input::KEY_STATES[VK_XBOXDPADRIGHT] = KEY_NEW_PRESS;
				}
				else
				{
					if(Snowstorm::Input::KEY_STATES[VK_XBOXDPADRIGHT])
						Snowstorm::Input::KEY_STATES[VK_XBOXDPADRIGHT] = KEY_NEW_RELEASE;
				}

				// zero trigger states
				Snowstorm::Engine::ev->xbox_rx = 0;
				Snowstorm::Engine::ev->xbox_ry = 0;
				Snowstorm::Engine::ev->xbox_lx = 0;
				Snowstorm::Engine::ev->xbox_ly = 0;
				Snowstorm::Engine::ev->xbox_rtr = 0;
				Snowstorm::Engine::ev->xbox_ltr = 0;


				// get trigger states
				float ltr = controller_state.Gamepad.bLeftTrigger;
				float rtr = controller_state.Gamepad.bRightTrigger;
				if(ltr > Snowstorm::Input::xbox_deadzone_trigger)
					Snowstorm::Engine::ev->xbox_ltr = ltr * 0.00390625f;
				if(rtr > Snowstorm::Input::xbox_deadzone_trigger)
					Snowstorm::Engine::ev->xbox_rtr = rtr * 0.00390625f;


				// get left stick states
				float lx = controller_state.Gamepad.sThumbLX;
				float ly = controller_state.Gamepad.sThumbLY;
				if(lx > Snowstorm::Input::xbox_deadzone_lp || lx < Snowstorm::Input::xbox_deadzone_ln)
					Snowstorm::Engine::ev->xbox_lx = lx * 0.000030517578125f;
				if(ly > Snowstorm::Input::xbox_deadzone_lp || ly < Snowstorm::Input::xbox_deadzone_ln)
					Snowstorm::Engine::ev->xbox_ly = ly * 0.000030517578125f;
				

				// get right stick states
				float rx = controller_state.Gamepad.sThumbRX;
				float ry = controller_state.Gamepad.sThumbRY;
				if(rx > Snowstorm::Input::xbox_deadzone_rp || rx < Snowstorm::Input::xbox_deadzone_rn)
					Snowstorm::Engine::ev->xbox_rx = rx * 0.000030517578125f;
				if(ry > Snowstorm::Input::xbox_deadzone_rp || ry < Snowstorm::Input::xbox_deadzone_rn)
					Snowstorm::Engine::ev->xbox_ry = ry * 0.000030517578125f;

			}
			else
			{
				// controller is not connected
				Snowstorm::Input::controller_states[i] = 0;
			}
		}
	}

	// finished
	return;
}

void Snowstorm::Input::XboxSetVibration(float left_motor, float right_motor)
{
	// create and fill vibration structure
	XINPUT_VIBRATION v;
	v.wLeftMotorSpeed = (WORD)(left_motor * 65535.0f);
	v.wRightMotorSpeed = (WORD)(right_motor * 65535.0f);

	// send it to each of the connected controllers
	for(int i = 0; i < XUSER_MAX_COUNT; i++)
	{
		// check if the controller is connected
		if(Snowstorm::Input::controller_states[i])
		{
			// send the data
			XInputSetState(i, &v);
		}
	}
}

void Snowstorm::Input::XboxSetDeadZones(float trigger, float left_stick, float right_stick)
{
	// trigger
	if(trigger != XBOX_DEADZONE_DONT_CHANGE)
	{
		if(trigger == XBOX_DEADZONE_DEFAULT)
			Snowstorm::Input::xbox_deadzone_trigger = XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
		else
			Snowstorm::Input::xbox_deadzone_trigger = trigger / 0.00390625f;
	}

	// left stick
	if(left_stick != XBOX_DEADZONE_DONT_CHANGE)
	{
		if(left_stick == XBOX_DEADZONE_DEFAULT)
		{
			Snowstorm::Input::xbox_deadzone_lp = XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE;
			Snowstorm::Input::xbox_deadzone_ln = XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE*-1;
		}
		else
		{
			Snowstorm::Input::xbox_deadzone_lp = left_stick / 0.000030517578125f;
			Snowstorm::Input::xbox_deadzone_ln = left_stick / -0.000030517578125f;
		}
	}

	// right stick
	if(right_stick != XBOX_DEADZONE_DONT_CHANGE)
	{
		if(right_stick == XBOX_DEADZONE_DEFAULT)
		{
			Snowstorm::Input::xbox_deadzone_rp = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE;
			Snowstorm::Input::xbox_deadzone_rn = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE*-1;
		}
		else
		{
			Snowstorm::Input::xbox_deadzone_rp = right_stick / 0.000030517578125f;
			Snowstorm::Input::xbox_deadzone_rn = right_stick / -0.000030517578125f;
		}
	}
}

////////////////////////////////////////////////////////////////////////////////////////////
// register event functions

SNOWSTORMXDLL_API void XRegisterKeyPressedEvent(EVENT event, uint8_t virtual_key)
{
	Snowstorm::Input::KEY_PRESS_EVENT_HANDLERS[virtual_key] = event;
	return;
}

SNOWSTORMXDLL_API void XRegisterKeyHeldEvent(EVENT event, uint8_t virtual_key)
{
	Snowstorm::Input::KEY_HELD_EVENT_HANDLERS[virtual_key] = event;
	return;
}

SNOWSTORMXDLL_API void XRegisterKeyReleasedEvent(EVENT event, uint8_t virtual_key)
{
	Snowstorm::Input::KEY_RELEASE_EVENT_HANDLERS[virtual_key] = event;
	return;
}

SNOWSTORMXDLL_API void XRegisterCloseClickEvent(EVENT event)
{
	Snowstorm::Input::WINDOW_CLOSE_EVENT_HANDLER = event;
	return;
}